/*
 * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Unlicense OR CC0-1.0
 */

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

#include "driver/gpio.h"

#include "task_pwm_capture.h"
#include "xlx_platform.h"

#define TASK_PWM_CAPTURE_OUT_STACK_SIZE (12*256)
static const char* TAG = "PwmCapture";
TaskHandle_t task_pwm_capture_handle;

#if 0
// 使用定时器捕获

#include "esp_private/esp_clk.h"
#include "driver/mcpwm_cap.h"

/*
 * pwm信号捕获
 * 算出pwm信号的频率和占空比
*/
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your board spec ////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define HC_SR04_TRIG_GPIO  27
#define HC_SR04_ECHO_GPIO  26

static void pm25_capture_init(void){
    ;
}


static bool hc_sr04_echo_callback(mcpwm_cap_channel_handle_t cap_chan, const mcpwm_capture_event_data_t *edata, void *user_data)
{
#if 1   
    static uint32_t cap_val_begin_of_sample = 0;
    static uint32_t cap_val_end_of_sample = 0;
    TaskHandle_t task_to_notify = (TaskHandle_t)user_data;
    BaseType_t high_task_wakeup = pdFALSE;

    //calculate the interval in the ISR,
    //so that the interval will be always correct even when capture_queue is not handled in time and overflow.
    if (edata->cap_edge == MCPWM_CAP_EDGE_POS) {
        // store the timestamp when pos edge is detected
        cap_val_begin_of_sample = edata->cap_value;
        cap_val_end_of_sample = cap_val_begin_of_sample;
    } else {
        cap_val_end_of_sample = edata->cap_value;
        uint32_t tof_ticks = cap_val_end_of_sample - cap_val_begin_of_sample;

        // notify the task to calculate the distance
        xTaskNotifyFromISR(task_to_notify, tof_ticks, eSetValueWithOverwrite, &high_task_wakeup);
    }

    return high_task_wakeup == pdTRUE;
#endif    
    return pdTRUE;
}

/**
 * @brief generate single pulse on Trig pin to start a new sample
 */
static void gen_trig_output(void)
{
    gpio_set_level(HC_SR04_TRIG_GPIO, 1); // set high
    esp_rom_delay_us(10);
    gpio_set_level(HC_SR04_TRIG_GPIO, 0); // set low
}


/*
 * 线程回调
*/
static void task_capture_cb(void *parameters){

    TUTU_LOGI(TAG, "task_id=%d\r\n", *((int *)parameters));
    TUTU_LOGI(TAG, "pwm_capture task is running...\r\n");

    TUTU_LOGI(TAG, "Install capture timer");
    mcpwm_cap_timer_handle_t cap_timer = NULL;
    mcpwm_capture_timer_config_t cap_conf = {
        .clk_src = MCPWM_CAPTURE_CLK_SRC_DEFAULT,
        // .clk_src = SOC_MOD_CLK_APB,
        //.clk_src = 4,
        .group_id = 0,
    };
    ESP_ERROR_CHECK(mcpwm_new_capture_timer(&cap_conf, &cap_timer));

#if 1
    TUTU_LOGI(TAG, "Install capture channel");
    mcpwm_cap_channel_handle_t cap_chan = NULL;
    mcpwm_capture_channel_config_t cap_ch_conf = {
        .gpio_num = HC_SR04_ECHO_GPIO,
        .prescale = 1,
        // capture on both edge
        .flags.neg_edge = true,
        .flags.pos_edge = true,
        // pull up internally
        .flags.pull_up = true,
    };
    ESP_ERROR_CHECK(mcpwm_new_capture_channel(cap_timer, &cap_ch_conf, &cap_chan));

    TUTU_LOGI(TAG, "Register capture callback");
    TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
    mcpwm_capture_event_callbacks_t cbs = {
        .on_cap = hc_sr04_echo_callback,
    };
    ESP_ERROR_CHECK(mcpwm_capture_channel_register_event_callbacks(cap_chan, &cbs, cur_task));

    TUTU_LOGI(TAG, "Enable capture channel");
    ESP_ERROR_CHECK(mcpwm_capture_channel_enable(cap_chan));

    TUTU_LOGI(TAG, "Configure Trig pin");
    gpio_config_t io_conf = {
        .mode = GPIO_MODE_OUTPUT,
        .pin_bit_mask = 1ULL << HC_SR04_TRIG_GPIO,
    };
    ESP_ERROR_CHECK(gpio_config(&io_conf));
    // drive low by default
    ESP_ERROR_CHECK(gpio_set_level(HC_SR04_TRIG_GPIO, 0));

    TUTU_LOGI(TAG, "Enable and start capture timer");
    ESP_ERROR_CHECK(mcpwm_capture_timer_enable(cap_timer));
    ESP_ERROR_CHECK(mcpwm_capture_timer_start(cap_timer));

    uint32_t tof_ticks;
    while (1) {
        // trigger the sensor to start a new sample
        gen_trig_output();
        // wait for echo done signal
        if (xTaskNotifyWait(0x00, ULONG_MAX, &tof_ticks, pdMS_TO_TICKS(1000)) == pdTRUE) {
            float pulse_width_us = tof_ticks * (1000000.0 / esp_clk_apb_freq());
            if (pulse_width_us > 35000) {
                // out of range
                continue;
            }
            // convert the pulse width into measure distance
            float distance = (float) pulse_width_us / 58;
            TUTU_LOGI(TAG, "Measured distance: %.2fcm", distance);
        }
        vTaskDelay(pdMS_TO_TICKS(500));
    }
#endif
    vTaskDelete(task_pwm_capture_handle);
}
#else
// 使用中断检测

#define PM25_PWM_INPUT_IO          (4) // Define the input GPIO

#define GPIO_INPUT_PIN_SEL  (1ULL<<PM25_PWM_INPUT_IO)
/*
 * Let's say, PM25_PWM_INPUT_IO=4
 * In binary representation,
 * 1ULL<<PM25_PWM_INPUT_IO is equal to 0000000000000000000000000000000000010000 
 * GPIO_INPUT_PIN_SEL                0000000000000000000000000000000000010000
 * */
#define ESP_INTR_FLAG_DEFAULT 0

typedef struct _PM25_PWM_{
    uint32_t gpio_num;      // 哪个 GPIO 
    uint32_t gpio_level;    // IO 高低电平
    uint32_t tick_count;    // 此时的tick
}pm25_msg_t;

static QueueHandle_t pm25_pwm_in_evt_queue = NULL;

static void IRAM_ATTR gpio_isr_handler(void* arg)
{
    pm25_msg_t msg;
    msg.gpio_num = (uint32_t) arg;
    msg.gpio_level = gpio_get_level(msg.gpio_num);
    msg.tick_count = xTaskGetTickCountFromISR();
    //msg.tick_count = xTaskGetTickCount();
    xQueueSendFromISR(pm25_pwm_in_evt_queue, &msg, NULL);
}

/*
 * pm2.5 的输出信号 捕获初始化
 * 使用GPIO4作为 信号输入
 * GPIO4配置输入，并使用中断，上升沿和下降沿都触发中断
 * 中断后使用全局变量记录 上升沿还是下降沿 并记录改时刻的tick，并发出消息
*/
static void pm25_capture_init(void){
    //zero-initialize the config structure.
    gpio_config_t io_conf = {};    
    //interrupt of rising edge
    io_conf.intr_type = GPIO_INTR_POSEDGE;
    //bit mask of the pins, use GPIO4 here
    io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
    //set as input mode
    io_conf.mode = GPIO_MODE_INPUT;
    //enable pull-up mode
    io_conf.pull_up_en = 0;
    gpio_config(&io_conf);

    //change gpio interrupt type for one pin
    gpio_set_intr_type(PM25_PWM_INPUT_IO, GPIO_INTR_ANYEDGE);

    //create a queue to handle gpio event from isr
    pm25_pwm_in_evt_queue = xQueueCreate(10, sizeof(pm25_msg_t));

    //install gpio isr service
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    //hook isr handler for specific gpio pin
    gpio_isr_handler_add(PM25_PWM_INPUT_IO, gpio_isr_handler, (void*) PM25_PWM_INPUT_IO);

    //remove isr handler for gpio number.
    gpio_isr_handler_remove(PM25_PWM_INPUT_IO);
    //hook isr handler for specific gpio pin again
    gpio_isr_handler_add(PM25_PWM_INPUT_IO, gpio_isr_handler, (void*) PM25_PWM_INPUT_IO);
}

/*
 * 线程回调
*/
static void task_capture_cb(void *parameters){
    pm25_msg_t msg, last={0};

    unsigned long int last_high_tick=1, last_low_tick=1;
    float duty;

    TUTU_LOGI(TAG, "task_id=%d\r\n", *((int *)parameters));
    TUTU_LOGI(TAG, "pwm_capture task is running...(portTICK_PERIOD_MS=%lu)\r\n", portTICK_PERIOD_MS);

    for (;;) {
        if (xQueueReceive(pm25_pwm_in_evt_queue, &msg, portMAX_DELAY)) {
            if(0 == msg.gpio_level){    // 低电平  // 一个周期                
                last_high_tick = msg.tick_count - last.tick_count;
                if(last_high_tick < 1){
                    last_high_tick = 1;
                }                

                // 计算出 占空比
                duty = last_high_tick*100.00 / (last_high_tick + last_low_tick);
                //TUTU_LOGI(TAG, "duty=%.2f%%,", duty);
                TUTU_LOGI(TAG, "GPIO[%lu] intr, val: %lu, tick: %lu(duty=%.4f%%,%lu,%lu)\n", 
                    msg.gpio_num, msg.gpio_level, msg.tick_count, 
                    duty, last_high_tick, last_low_tick);  
                }
            // }
            else {              // 高电平            
                last_low_tick = msg.tick_count - last.tick_count;
                if(last_low_tick < 1){
                    last_low_tick = 1;
                }
                
                // TUTU_LOGI(TAG, "GPIO[%lu] intr, val: %lu, tick: %lu, high: %lu, low: %lu\n", 
                //         msg.gpio_num, msg.gpio_level, msg.tick_count, 
                //         last_high_tick, last_low_tick);                  
            }
            last = msg;  
        }
    }

    // while(1){       
    //     TUTU_LOGI(TAG, "~");
    //     vTaskDelay(2000 / portTICK_PERIOD_MS); 
    // }

    vTaskDelete(task_pwm_capture_handle);
}    
#endif

const int task4_id = 104;
void task_pwm_capture_init(void)
{ 
    int ret;

    pm25_capture_init();

    ret = xTaskCreate(task_capture_cb, "pwm_capture task", TASK_PWM_CAPTURE_OUT_STACK_SIZE,
                    (void *)&task4_id, 6, &task_pwm_capture_handle);
    
    if (ret != pdTRUE) {
        TUTU_LOGE(TAG, "%s(ret=%d)\r\n", "pwm_capture task create fail!", ret);        

    }
    else {
         TUTU_LOGI(TAG, "%s(ret=%d)\r\n", "pwm_capture task create OK", ret);
    }  
}
